# 描述: 日出日落采集器
# 作者: 王茂林
# 时间: 2024/3/26 14:02

import concurrent.futures
import json
import time
import traceback
import uuid
import json
import datetime
from astral import LocationInfo
from astral.sun import sun

from common.MQTT import MQTT
import common.logger as logger
import common.utils as utils


class RunrisetCollector:

    def __init__(self, app_name='iori-runriset', **kwargs):
        # 纬度
        self.latitude = float(kwargs.get("latitude"))
        # 经度
        self.longitude = float(kwargs.get("longitude"))
        # MQTT连接参数
        self.client_id = kwargs.get("client_id")
        self.username = kwargs.get("username")
        self.pwd = kwargs.get("pwd")
        self.host = kwargs.get("host")
        self.port = kwargs.get("port")
        self.mqtt_client = None
        # 初始化MQTT连接
        self.init_mqtt()

    def init_mqtt(self):
        """
        初始化连接MQTT
        :return:
        """
        try:
            self.mqtt_client = MQTT(self.client_id, self.username, self.pwd, self.host, self.port)
            self.mqtt_client.connect()
        except Exception as e:
            logger.error(f"MQTT连接失败: {repr(e)}, 10秒后将再次尝试")
            # 10秒尝试重连一次
            time.sleep(10)
            self.init_mqtt()

    def report_heartbeat(self):
        """
        上报MQTT心跳到Rlink
        :return:
        """
        while True:
            try:
                # 每隔3分钟上报一次
                time.sleep(180)
                # 上报的数据
                report_data = {
                    "requestId": str(uuid.uuid1()),
                    "version": "1.0",
                    "sys": {
                        "ack": 0
                    },
                    "params": {
                        "reportTime": utils.get_iot_report_time()
                    }
                }
                # 获取上报心跳的topic
                report_topic = self.build_topic('heartbeat')
                payload = json.dumps(report_data)
                self.mqtt_client.publish(report_topic, payload)
                logger.debug(f'发送心跳[{report_topic}]：{payload}')
            except Exception as e:
                logger.error(f"上报心跳失败: {repr(e)}！！！")
                traceback.print_exc()

    def build_topic(self, action_type):
        """
        构建对应类型的topic
        :return:
        """
        match action_type:
            case 'property':  # 上报属性
                return f"sys/{self.username.replace('_','/')}/thing/device/property/report"
            case _:  # 默认上报心跳
                return f"sys/{self.username.replace('_', '/')}/thing/device/login"

    def report_data(self):
        """
        上报数据到Rlink
        :return:
        """
        while True:
            try:
                # 获取日出日落数据
                # 时区
                timezone = 'Asia/Shanghai'
                # 定位
                location = LocationInfo('Shenzhen', 'China', timezone, self.latitude, self.longitude)
                # 计算今天的日出时间
                s = sun(location.observer, date=datetime.date.today(), tzinfo=location.timezone)
                # 日出时间
                sunrise_time = s['sunrise'].strftime('%Y-%m-%d %H:%M:%S')
                # 日落时间
                sunset_time = s['sunset'].strftime('%Y-%m-%d %H:%M:%S')
                report_data = {
                    "requestId": str(uuid.uuid1()),
                    "version": "1.0",
                    "params": {
                        "modules": [
                            {
                                "moduleName": "default",
                                "properties": {
                                    "sunrise": sunrise_time,
                                    "sunset": sunset_time
                                },
                                "reportTime": utils.get_iot_report_time()
                            }
                        ]
                    }
                }

                if report_data is None:
                    # 取不到,则进行下一次循环
                    continue

                # 获取上报数据的topic
                report_topic = self.build_topic('property')
                payload = json.dumps(report_data)
                self.mqtt_client.publish(report_topic, payload)
                logger.debug(f'发送数据[{report_topic}]：{payload}')
            except Exception as e:
                logger.error(f"上报数据失败: {repr(e)}！！！")
                traceback.print_exc()
            # 每隔1小时上报一次
            time.sleep(3600)
        # 断开连接
        self.mqtt_client.disconnect()

    def __call__(self):
        def handle_exception(worker):
            """
            线程中的运行时异常，统一异常处理
            :param worker:
            :return:
            """
            try:
                worker.result()
            except Exception as e:
                logger.error(e)
                traceback.print_exc()

        # 起线程处理任务
        with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
            # 平台心跳
            executor.submit(self.report_heartbeat).add_done_callback(handle_exception)
            # 采集上报数据
            executor.submit(self.report_data).add_done_callback(handle_exception)

